#pragma once

#include <iostream>
#include <string>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <pthread.h>
#include <cstdio>
#include <time.h>
#include <memory>
#include <unistd.h>

#include "Mutex.hpp"

namespace LogModule
{
    using namespace MutexModule;

    #define LOG(Level) (plog(__FILE__ , __LINE__ , Level))
    #define SET_CONSOLE_LOG (plog.SetConsoleStrategy())
    #define SET_FILE_LOG (plog.SetFileStrategy())
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL // 致命

    };

    const std::string default_path = "./LOG/";
    const std::string default_name = "log.txt";

    class TopStrategy
    {
    public:
        virtual ~TopStrategy() = default;
        virtual void Strategy(const std::string &message) = 0;
    };

    class ConsoleStrategy : public TopStrategy
    {
    public:
        ConsoleStrategy()
        {}
        virtual void Strategy(const std::string &message)
        {
            MutexGuard lock(_mutex);
            std::cout << message << std::endl;
        }

    private:
        Mutex _mutex;
    };

    class FileStrategy : public TopStrategy
    {
    public:
        FileStrategy(const std::string &filepath = default_path, const std::string &filename = default_name)
            : _logpath(filepath), _logname(filename)
        {
            if (std::filesystem::exists(_logpath))
            {
                return;
            }
            try
            {
                std::filesystem::create_directory(_logpath);
            }
            catch (const std::exception &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        virtual void Strategy(const std::string &message)
        {
            MutexGuard lock(_mutex);
            std::ofstream out(_logpath + _logname , std::ios_base::app);

            out << message << std::endl;
        }

    private:
        std::string _logpath;
        std::string _logname;

        Mutex _mutex;
    };

    class Log : public ConsoleStrategy, FileStrategy
    {
    public:
        Log()
        {
            _strategy = std::make_shared<ConsoleStrategy>();
        }

        void Flush(const std::string& message)
        {
            _strategy->Strategy(message);
        }

        void SetFileStrategy()
        {
            _strategy = std::make_shared<FileStrategy>();
        }

        void SetConsoleStrategy()
        {
            _strategy = std::make_shared<ConsoleStrategy>();
        }

        class Message
        {
        private:
            std::string CurrentTime()
            {
                char msg[1024];
                time_t timer = time(NULL);
                struct tm time;
                localtime_r(&timer, &time);
                snprintf(msg, sizeof(msg), "%4d-%02d-%02d-%02d-%02d", time.tm_year + 1900, time.tm_mon + 1, time.tm_mday, time.tm_hour , time.tm_min);
                return msg;
            }

        public:
            Message(std::string filename, int file_line , Log& log , LogLevel loglevel = LogLevel::DEBUG)
                : _time(CurrentTime())
                , _loglevel(loglevel)
                , _pid(getpid())
                , _tid(gettid())
                , _filename(filename)
                , _fileline(file_line)
                , _log(log)
            {
                char msg[1024];
                std::string s;
                switch (_loglevel)
                {
                case LogLevel::DEBUG:
                    s = "DEBUG";
                    break;
                case LogLevel::INFO:
                    s = "INFO";
                    break;
                case LogLevel::WARNING:
                    s = "WARNING";
                    break;
                case LogLevel::ERROR:
                    s = "ERROR";
                    break;
                case LogLevel::FATAL:
                    s = "FATAL";
                    break;
                default:
                    s = "LOG_ERROR";
                    break;
                }
                snprintf(msg, sizeof(msg), "【%s】【%s】【%d】【%ld】【%s】【%d】- ", _time.c_str() , s.c_str() , _pid , _tid , _filename.c_str(),_fileline);
                _message += msg;
            }

            template<typename T>
            Message& operator<<(const T& info)
            {
                std::ostringstream sout;
                sout << info;
                _message += sout.str();
                return *this;
            }

            ~Message()
            {
                if(!_message.empty())
                {
                    _log.Flush(_message);
                }
            }



        private:
            //**【可读性很好的时间】【日志等级】【进程pid】【线程tid】【打印对应日志的文件名】【行号】-消息内容，支持 可变参数**、
            std::string _time;
            LogLevel _loglevel;
            pid_t _pid;
            pthread_t _tid;
            std::string _filename;
            int _fileline;
            Log& _log;
            std::string _message;
        };

        Message operator()(std::string filename , int fileline , LogLevel level)
        {
            Message message(filename,fileline,*this , level);
            return message;
        }

    private:
        std::shared_ptr<TopStrategy> _strategy;
    };

    Log plog;
}